﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;

namespace QQSideShowCreator {
	partial class MainForm {
		/// <summary>
		/// Initialize properties about editing the picture.
		/// This is called when program starts.
		/// </summary>
		private void InitializePictureEditComponents() {
			rectViewport = new Rectangle(Point.Empty, pictureBox1.Size);
			imgViewport = new Bitmap(pictureBox1.Width, pictureBox1.Height);
		}

		protected void RenewProfileForEditing(ref Bitmap frame_image) {
			imgFrame = frame_image;

			// After applying a new profile, reset all parameters to make sure the picture still
			// cover the whole viewport.
			// Load a new frame then
			if ( imgViewport != null )
				imgViewport.Dispose();
			rectViewport = new Rectangle(Point.Empty, pictureBox1.Size);
			imgViewport = new Bitmap(pictureBox1.Width, pictureBox1.Height);	// a new viewport image of new size

			if ( imgSource != null ) {
				caculateScaleBound();						// new picture bounds for new picturebox size
				setTrackbarParameters(MAX_SCALE, MIN_SCALE);// set trackbar for new picture scaling limits
				fScale = amendZoomingScale(fScale);			// new zooming scale under new scaling limits
				amendViewportCoordinate();					// move picture source to cover whole viewport
			}
		}

		// viewport display parameters
		float fScale = 1.0f;
		Point pointViewportTopLeft = new Point(0, 0); // related to the source image
		public Point ViewPortTopLeft {
			get { return pointViewportTopLeft; }
			set {
				pointViewportTopLeft = value;
				amendViewportCoordinate();

				// refresh UI elements
				RenderPictureBox();
				labelLeftTop.Text = pointViewportTopLeft.ToString();
			}
		}

		// status of drag
		bool bDragLock = false;
		Point pointOldViewportTopLeft;
		Point pointMouseDown;

		// Zooming
		/// <summary>
		/// the multiplier is used to convert the scale to an integer for trackBar
		/// </summary>
		const int SCALE_TO_INT = 100; 
		/// <summary>
		/// trackbar has 5 region, chosen arbitrarily
		/// </summary>
		const int TICK_FREQUENCY = 5;

		float MIN_SCALE;
		float MAX_SCALE;

		// image resource
		Image imgSource;
		Image imgViewport;
		Rectangle rectViewport;

		Bitmap imgFrame;

		public float ZoomingScale {
			get { return fScale; }
			set {   // change viewport
				fScale = value;
				fScale = amendZoomingScale(fScale);
				amendViewportCoordinate();

				// do the render right now
				RenderPictureBox();

				// UI elements revised
				labelZoomX.Text = fScale.ToString() + " X";
				labelLeftTop.Text = ViewPortTopLeft.ToString();
				int nNewValue = Convert.ToInt32(fScale * SCALE_TO_INT);
				if ( nNewValue < trackBar1.Minimum )
					nNewValue = trackBar1.Minimum;
				trackBar1.Value = nNewValue;
			}
		}

		protected void RenderPictureBox() {
			// firstly copy 
			Graphics g = Graphics.FromImage(imgViewport);

			// set the image quality
			g.CompositingQuality = CompositingQuality.HighQuality;
			g.SmoothingMode = SmoothingMode.HighQuality;
			g.InterpolationMode = InterpolationMode.HighQualityBicubic;

			// to strech the source image 
			g.DrawImage(imgSource, rectViewport,
				pointViewportTopLeft.X, pointViewportTopLeft.Y,
				pictureBox1.Width / fScale, pictureBox1.Height / fScale,
				GraphicsUnit.Pixel);

			// if frame is needed to be shown
			if ( checkPreviewOn.Checked ) {
				g = Graphics.FromImage(imgViewport);
				g.CompositingQuality = CompositingQuality.HighQuality;
				g.SmoothingMode = SmoothingMode.HighQuality;
				g.InterpolationMode = InterpolationMode.HighQualityBicubic;

				g.DrawImage(imgFrame, rectViewport, 0, 0, imgFrame.Width, imgFrame.Height, GraphicsUnit.Pixel);
			}
			g.Dispose();
			GC.Collect();

			pictureBox1.Image = imgViewport;
			pictureBox1.Show();
		}

		protected void CleanUp() {
			if ( imgFrame != null )
				imgFrame.Dispose();
			if ( imgSource != null )
				imgSource.Dispose();
			if ( imgViewport != null )
				imgViewport.Dispose();
		}

		/// <summary>
		/// Open a file to edit
		/// </summary>
		/// <param name="file_path">Full filename of an image to open. Relative path is OK.</param>
		/// <returns>True if file is acceptable. False if file format cannot recognize or is refused to open.</returns>
		protected bool OpenImageFile(string file_path) {
			try {
				if ( imgSource != null )
					imgSource.Dispose();
				imgSource = Image.FromFile(file_path);

				// set the trackbar, which accept integers only
				caculateScaleBound();
				setTrackbarParameters(MAX_SCALE, MIN_SCALE);

				ZoomingScale = 1.0f;

				return true;
			} catch { // if file is not actually supported
				if ( imgSource != null )
					imgSource.Dispose();
				return false;
			}
		}

		#region Help Functions
		/// <summary>
		/// To work out the minimal and maximal scale, after opening an image. 
		/// This is based on the original image size and assures that the viewport is
		/// always covered by some part of the original image when any scale in the 
		/// range is applied.
		/// </summary>
		protected void caculateScaleBound() {
			// The minimal scale is the larger one that makes the image fit the pictureBox1,
			// because the other side, say width, will not long enough to fit the width of 
			// the picturebox, if the scale on height is less and chosen as the minimal one.
			float fWidthScale = pictureBox1.Width * 1.0f / imgSource.Width;
			float fHeightScale = pictureBox1.Height * 1.0f / imgSource.Height;
			MIN_SCALE = fWidthScale > fHeightScale ? fWidthScale : fHeightScale;

			// caculate & set the maximal scale.
			// Maximal scale is set just to make sure the trackBar control is divided to
			// 5(=TICK_FREQUENCY) regions.
			if ( MIN_SCALE > 1 ) {
				MAX_SCALE = MIN_SCALE * TICK_FREQUENCY;
			} else {
				MAX_SCALE = 1.0f / MIN_SCALE * 1.0f;
			}

		}

		/// <summary>
		/// If the zooming scale is set invalidly, change it to the valid number range
		/// </summary>
		protected float amendZoomingScale(float fScale) {
			if ( fScale < MIN_SCALE )
				fScale = MIN_SCALE;
			if ( fScale > MAX_SCALE )
				fScale = MAX_SCALE;
			return fScale;
		}

		protected void setTrackbarParameters(float max_scale, float min_scale) {
			trackBar1.Minimum = Convert.ToInt32(MIN_SCALE * SCALE_TO_INT);
			if ( trackBar1.Minimum < MIN_SCALE * SCALE_TO_INT )
				trackBar1.Minimum += 1;
			trackBar1.Maximum = Convert.ToInt32(MAX_SCALE * SCALE_TO_INT);
			trackBar1.TickFrequency = ( trackBar1.Maximum - trackBar1.Minimum )
				/ TICK_FREQUENCY;
		}

		/// <summary>
		/// This function makes sure the viewport still covers the whole picture box,
		/// even when rescaling.
		/// </summary>
		/// <returns>True if the viewport needs and is amended; false if no amendment is needed</returns>
		protected bool amendViewportCoordinate() {
			bool bAutoAmended = false;
			// the viewing
			if ( pointViewportTopLeft.X < 0 ) {
				pointViewportTopLeft.X = 0;
				bAutoAmended = true;
			}
			if ( pointViewportTopLeft.Y < 0 ) {
				pointViewportTopLeft.Y = 0;
				bAutoAmended = true;
			}
			if ( pictureBox1.Width >
				Math.Abs(imgSource.Width - pointViewportTopLeft.X) * fScale ) {
				pointViewportTopLeft.X = Convert.ToInt32(imgSource.Width - pictureBox1.Width / fScale);
				bAutoAmended = true;
			}
			if ( pictureBox1.Height >
				Math.Abs(imgSource.Height - pointViewportTopLeft.Y) * fScale ) {
				pointViewportTopLeft.Y = Convert.ToInt32(imgSource.Height - pictureBox1.Height / fScale);
				bAutoAmended = true;
			}

			return bAutoAmended;
		}

		#endregion
	}
}
